
package com.content.scaffold.common.web;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.ttl.TransmittableThreadLocal;

import com.content.scaffold.common.constants.RedisKey;
import com.content.scaffold.common.utils.RedisUtils;
import com.content.scaffold.model.enums.UserTypeEnum;
import com.content.scaffold.model.dto.login.LoginUser;
import com.content.scaffold.model.dto.login.UserInfo;
import lombok.Getter;
import org.slf4j.MDC;

import java.util.*;

import static com.content.scaffold.common.constants.RedisKey.LOGIN_USER_EXPIRE_TIME;

/**
 * @author xiaojl
 */
public class RequestContext {

    private static final TransmittableThreadLocal<RequestInfo> THREAD_LOCAL = new TransmittableThreadLocal<>();
    public static final String ANONYMOUS = "anonymous";
    private static final Set<String> ADMIN_ROLES = new HashSet<>(Arrays.asList("ADMIN_GJ", "ADMIN"));

    @Getter
    private static RedisUtils<String> redisUtils;

    public static void setRedisUtils(RedisUtils<String> redisUtils) {
        RequestContext.redisUtils = redisUtils;
    }


    public static RequestInfo getRequestInfo() {
        return THREAD_LOCAL.get();
    }

    public static void clear() {
        THREAD_LOCAL.remove();
    }

    public static void setRequestInfo(RequestInfo requestInfo) {
        THREAD_LOCAL.set(requestInfo);
    }

    public static void buildRequestInfo(LoginUser loginUser) {
        if (loginUser == null) {
            return;
        }

        UserInfo userInfo = loginUser.getUserInfo();
        if (userInfo == null) {
            return;
        }
        List<String> roleCodes = userInfo.getRoleCodes();
        boolean isAdmin = false;
        if (roleCodes != null && !roleCodes.isEmpty()) {
            isAdmin = roleCodes.stream().anyMatch(ADMIN_ROLES::contains);
        }

        RequestInfo requestInfo = buildRequestInfoInternal(loginUser, isAdmin);
        setRequestInfo(requestInfo);
    }

    private static RequestInfo buildRequestInfoInternal(LoginUser loginUser, boolean isAdmin) {
        UserInfo userInfo = loginUser.getUserInfo();
        return RequestInfo.builder()
                .realName(userInfo.getUserName())
                .userId(userInfo.getId())
                .userName(userInfo.getUserCode())
                .deptId(userInfo.getDeptId())
                .appId(userInfo.getApplicationId())
                .token(loginUser.getToken())
                .roles(userInfo.getRoleCodes() != null ? userInfo.getRoleCodes() : Collections.emptyList())
                .admin(isAdmin)
                .userType(UserTypeEnum.getUserTypeEnum(userInfo.getUserType()))
                .build();
    }

    public static LoginUser getLoginUser(String token) {
        if (token == null) {
            return null;
        }
        String key = RedisKey.getLoginUserKey(token);
        String loginUserStr = redisUtils.getString(key);
        if (ObjectUtil.isEmpty(loginUserStr)) {
            return null;
        }
        return JSONUtil.toBean(loginUserStr, LoginUser.class);
    }

    public static void extendLoginExpireTime(String token) {
        String key = RedisKey.getLoginUserKey(token);
        redisUtils.expire(key, LOGIN_USER_EXPIRE_TIME);
    }

    public static LoginUser getLoginUser() {
        RequestInfo requestInfo = getRequestInfo();
        if (requestInfo == null) {
            return null;
        }
        String token = requestInfo.getToken();
        return getLoginUser(token);
    }

    public static void putLoginUser(LoginUser user) {
        String key = RedisKey.getLoginUserKey(user.getToken());
        redisUtils.set(key, JSONUtil.toJsonStr(user), LOGIN_USER_EXPIRE_TIME);
    }

    public static void logout() {
        RequestInfo requestInfo = getRequestInfo();
        if (requestInfo == null) {
            return;
        }
        String token = requestInfo.getToken();
        String key = RedisKey.getLoginUserKey(token);
        redisUtils.del(key);
    }

    public static final RequestInfo ANONYMOUS_REQUEST_INFO = RequestInfo.builder()
            .officeId(ANONYMOUS)
            .realName(ANONYMOUS)
            .userId(ANONYMOUS)
            .userName(ANONYMOUS)
            .token(ANONYMOUS)
            .build();

    public static RequestInfo buildAnonymous() {
        return ANONYMOUS_REQUEST_INFO;
    }

    public static void setupMdc() {
        RequestInfo info = getRequestInfo();
        if (info != null) {
            MDC.put("traceId", info.getTraceId());
            MDC.put("userId", info.getUserId());
            MDC.put("userName", info.getUserName());
        }
    }

    public static void clearMdc() {
        MDC.clear();
    }
}